#include <stdio.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#include "cluster.h"
#include "lichstor.h"
#include "adt.h"
#include "dirent.h"
#include "fileinfo.h"
#include "fs_proto.h"
#include "chunk.h"
#include "lich_md.h"
#include "lobject.h"
#include "ltable.h"
#include "lua.h"
#include "utils.h"

#define STACK_IDX_2 (-2)
#define STACK_IDX_3 (-3)


int l_env_init_simple(lua_State* L) {
    const char *s = luaL_checkstring(L, 1);
    lua_pushnumber(L, env_init_simple(s));
    return 1;
}

int l_network_connect_master(lua_State* L) {
    lua_pushnumber(L,network_connect_master());
    return 1;
}

int l_stor_init(lua_State* L) {
    const char *s = luaL_checkstring(L, 1);
    int num = luaL_checknumber(L, 2);
    lua_pushnumber(L, stor_init(s,num));
    return 1;
}

int l_node_getinfo(lua_State* L) {
    nodeinfo_t *info;
    char buf[4096];
    const char *name = luaL_checkstring(L, 1);
    int ret;

    info = (void *)buf;

    ret = node_getinfo(info, name);
    if (unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }

    lua_newtable(L);

    lua_pushstring(L, "clustername");
    lua_pushstring(L, info->clustername);
    lua_settable(L, -3);

    lua_pushstring(L, "nodename");
    lua_pushstring(L, info->nodename);
    lua_settable(L, -3);

    lua_pushstring(L, "nid");
    lua_pushnumber(L, info->stat->nid.id);
    lua_settable(L, -3);

    lua_pushstring(L, "writeable");
    info->stat->status & __NODE_STAT_WRITEABLE__ ? lua_pushboolean(L, 1) : lua_pushboolean(L, 0);
    lua_settable(L, -3);

    lua_pushstring(L, "ready");
    info->stat->status & __NODE_STAT_READY__ ? lua_pushboolean(L, 1) : lua_pushboolean(L, 0);
    lua_settable(L, -3);

    lua_pushstring(L, "capacity");
    lua_pushnumber(L, info->stat->total);
    lua_settable(L, -3);

    //TODO used is diffrent than stat
    lua_pushstring(L, "used");
    lua_pushnumber(L, info->stat->used);
    lua_settable(L, -3);

    lua_pushstring(L, "load");
    lua_pushnumber(L, info->stat->load);
    lua_settable(L, -3);

    lua_pushstring(L, "admin");
    lua_pushstring(L, info->admin);
    lua_settable(L, -3);

    lua_pushstring(L, "status");
    lua_pushstring(L, info->status);
    lua_settable(L, -3);

    lua_pushstring(L, "home");
    lua_pushstring(L, info->home);
    lua_settable(L, -3);

    lua_pushstring(L, "metas");
    lua_pushstring(L, info->quorum);
    lua_settable(L, -3);

    lua_pushstring(L, "uptime");
    lua_pushnumber(L, *info->uptime);
    lua_settable(L, -3);
    return 1;
}

int l_admin_listnode(lua_State* L) {
    int buflen, done = 0;
    char buf[MAX_BUF_LEN], tmp[MAX_BUF_LEN];
    uint64_t offset = 0, offset2 = 0;
    struct dirent *de;
    nodeinfo_t *info;
    uuid_t _uuid;
    char uuid[UUID_LEN] = {};
    int i = 1;

    lua_newtable(L);

    uuid_generate(_uuid);
    uuid_unparse(_uuid, uuid);

    cluster_listnode_open(uuid);

    while (done == 0) {
        memset(buf, 0, sizeof(buf));
        cluster_listnode(buf, &buflen, uuid, offset);

        if (buflen == 0) {
            break;
        }

        offset2 = 0;
        info = (void *)tmp;
        dir_for_each(buf, buflen, de, offset2) {
            if (strlen(de->d_name) == 0) {
                done = 1;
                break;
            } else if (buflen - offset2 < sizeof(*de) + MAX_NAME_LEN)
                break;

            offset += de->d_reclen;

            node_getinfo(info, de->d_name);

            lua_pushnumber(L, i++);
            lua_pushstring(L, de->d_name);
            lua_settable(L, STACK_IDX_3);
        }
    }


    cluster_listnode_close(uuid);

    return 1;
}


int l_net_gethostname(lua_State* L)
{
	int ret;
	char hostname[MAX_NAME_LEN];

	ret = net_gethostname(hostname, 0);
	if (unlikely(ret)) {
            lua_pushnil(L);
            return 1;
	}

	lua_pushstring(L, hostname);
	return 1;
}

int l_dispatch_sysstat(lua_State* L)
{
    int ret, force = 0;
    lich_stat_t stat;

    ret = dispatch_sysstat(&stat, force);
    if (unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }

    lua_newtable(L);

    lua_pushnumber(L, stat.site_total);
    lua_setfield(L, STACK_IDX_2, "site_total");

    lua_pushnumber(L, stat.site_online);
    lua_setfield(L, STACK_IDX_2, "site_online");

    lua_pushnumber(L, stat.rack_total);
    lua_setfield(L, STACK_IDX_2, "rack_total");

    lua_pushnumber(L, stat.rack_online);
    lua_setfield(L, STACK_IDX_2, "rack_online");

    lua_pushnumber(L, stat.node_total);
    lua_setfield(L, STACK_IDX_2, "node_total");

    lua_pushnumber(L, stat.node_online);
    lua_setfield(L, STACK_IDX_2, "node_online");

    lua_pushnumber(L, stat.disk_total);
    lua_setfield(L, STACK_IDX_2, "disk_total");

    lua_pushnumber(L, stat.disk_online);
    lua_setfield(L, STACK_IDX_2, "disk_online");

    return 1;
}

void send_fileinfo_table(lua_State* L, fileinfo_t fileinfo) {
    int i;

    lua_newtable(L);

    lua_newtable(L);
    lua_pushinteger(L, fileinfo.id.id);
    lua_setfield(L, STACK_IDX_2, "id");
    lua_pushinteger(L, fileinfo.id.type);
    lua_setfield(L, STACK_IDX_2, "type");
    lua_pushinteger(L, fileinfo.id.idx);
    lua_setfield(L, STACK_IDX_2, "idx");
    lua_setfield(L, STACK_IDX_2, "id");

    lua_pushinteger(L, fileinfo.magic);
    lua_setfield(L, STACK_IDX_2, "magic");

    lua_pushinteger(L, fileinfo.repnum);
    lua_setfield(L, STACK_IDX_2, "repnum");

    lua_pushinteger(L, fileinfo.snap_rollback);
    lua_setfield(L, STACK_IDX_2, "snap_rollback");

    lua_pushinteger(L, fileinfo.snap_version);
    lua_setfield(L, STACK_IDX_2, "snap_version");

    lua_pushinteger(L, fileinfo.reference);
    lua_setfield(L, STACK_IDX_2, "reference");

    lua_pushinteger(L, fileinfo.attr);
    lua_setfield(L, STACK_IDX_2, "attr");

    lua_pushinteger(L, fileinfo.priority);
    lua_setfield(L, STACK_IDX_2, "priority");

    lua_newtable(L);
    for(i = 0; i < 4; i++)
    {
        lua_pushinteger(L, i+1);
        lua_pushinteger(L, fileinfo.__pad__[i]);
        lua_settable(L, -3);
    }
    lua_setfield(L, STACK_IDX_2, "__pad__");

    lua_pushinteger(L, fileinfo.size);
    lua_setfield(L, STACK_IDX_2, "size");

    lua_pushinteger(L, fileinfo.mode);
    lua_setfield(L, STACK_IDX_2, "mode");

    lua_pushinteger(L, fileinfo.uid);
    lua_setfield(L, STACK_IDX_2, "uid");

    lua_pushinteger(L, fileinfo.gid);
    lua_setfield(L, STACK_IDX_2, "gid");

    lua_pushinteger(L, fileinfo.ctime);
    lua_setfield(L, STACK_IDX_2, "ctime");

    lua_pushinteger(L, fileinfo.mtime);
    lua_setfield(L, STACK_IDX_2, "mtime");

    lua_pushinteger(L, fileinfo.btime);
    lua_setfield(L, STACK_IDX_2, "btime");

    lua_pushinteger(L, fileinfo.atime);
    lua_setfield(L, STACK_IDX_2, "atime");

}

void send_setattr_table(lua_State* L, setattr_t* setattr) {
    lua_newtable(L);

    lua_newtable(L);
    lua_pushnumber(L, setattr->mode.mode);
    lua_setfield(L, STACK_IDX_2, "mode");
    lua_pushnumber(L, setattr->mode.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"mode");

    lua_newtable(L);
    lua_pushnumber(L, setattr->uid.uid);
    lua_setfield(L, STACK_IDX_2, "uid");
    lua_pushnumber(L, setattr->uid.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"uid");

    lua_newtable(L);
    lua_pushnumber(L, setattr->gid.gid);
    lua_setfield(L, STACK_IDX_2, "gid");
    lua_pushnumber(L, setattr->gid.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"gid");

    lua_newtable(L);
    lua_pushnumber(L, setattr->size.size);
    lua_setfield(L, STACK_IDX_2, "size");
    lua_pushnumber(L, setattr->size.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"size");

    lua_newtable(L);
    lua_pushnumber(L, setattr->atime.time.seconds);
    lua_setfield(L, STACK_IDX_2, "seconds");
    lua_pushnumber(L, setattr->atime.time.nseconds);
    lua_setfield(L, STACK_IDX_2, "nseconds");
    lua_pushnumber(L, setattr->atime.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"atime");

    lua_newtable(L);
    lua_pushnumber(L, setattr->btime.time.seconds);
    lua_setfield(L, STACK_IDX_2, "seconds");
    lua_pushnumber(L, setattr->btime.time.nseconds);
    lua_setfield(L, STACK_IDX_2, "nseconds");
    lua_pushnumber(L, setattr->btime.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"btime");

    lua_newtable(L);
    lua_pushnumber(L, setattr->ctime.time.seconds);
    lua_setfield(L, STACK_IDX_2, "seconds");
    lua_pushnumber(L, setattr->ctime.time.nseconds);
    lua_setfield(L, STACK_IDX_2, "nseconds");
    lua_pushnumber(L, setattr->ctime.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"ctime");

    lua_newtable(L);
    lua_pushnumber(L, setattr->mtime.time.seconds);
    lua_setfield(L, STACK_IDX_2, "seconds");
    lua_pushnumber(L, setattr->mtime.time.nseconds);
    lua_setfield(L, STACK_IDX_2, "nseconds");
    lua_pushnumber(L, setattr->mtime.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"mtime");

    lua_newtable(L);
    lua_pushnumber(L, setattr->replica.replica);
    lua_setfield(L, STACK_IDX_2, "replica");
    lua_pushnumber(L, setattr->replica.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"replica");

    lua_newtable(L);
    lua_pushnumber(L, setattr->clone.clone);
    lua_setfield(L, STACK_IDX_2, "clone");
    lua_pushnumber(L, setattr->clone.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"clone");

    lua_newtable(L);
    lua_pushnumber(L, setattr->protect.protect);
    lua_setfield(L, STACK_IDX_2, "protect");
    lua_pushnumber(L, setattr->protect.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"protect");

    lua_newtable(L);
    lua_pushnumber(L, setattr->priority.priority);
    lua_setfield(L, STACK_IDX_2, "priority");
    lua_pushnumber(L, setattr->priority.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"priority");

    lua_newtable(L);
    lua_pushnumber(L, setattr->localize.localize);
    lua_setfield(L, STACK_IDX_2, "localize");
    lua_pushnumber(L, setattr->localize.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"localize");

    lua_newtable(L);
    lua_pushnumber(L, setattr->multpath.multpath);
    lua_setfield(L, STACK_IDX_2, "multpath");
    lua_pushnumber(L, setattr->multpath.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"multpath");

    lua_newtable(L);
    lua_pushnumber(L, setattr->writeback.writeback);
    lua_setfield(L, STACK_IDX_2, "writeback");
    lua_pushnumber(L, setattr->writeback.set_it);
    lua_setfield(L, STACK_IDX_2, "set_it");
    lua_setfield(L,-2,"writeback");

}

void send_fileid_table(lua_State* L, fileid_t fid){
    lua_newtable(L);

    lua_pushinteger(L, fid.id);
    lua_setfield(L, STACK_IDX_2, "id");

    lua_pushinteger(L, fid.type);
    lua_setfield(L, STACK_IDX_2, "type");

    lua_pushinteger(L, fid.idx);
    lua_setfield(L, STACK_IDX_2, "idx");
}

void fileinfo_table(lua_State* L,int idx,fileinfo_t* fileinfo, const char *name) {
    int i;
    lua_pushnil(L);
    while(lua_next(L, idx))
    {
        if(lua_type(L, -1) != LUA_TTABLE) {
            if (lua_type(L, -2) == LUA_TNUMBER) {
                i = lua_tointeger(L, -2);
                fileinfo->__pad__[i - 1] = lua_tointeger(L, -1);
            } else {
                if (strcmp(lua_tostring(L, -2), "id") == 0)
                    fileinfo->id.id = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "type") == 0)
                    fileinfo->id.type = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "idx") == 0)
                    fileinfo->id.idx = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "magic") == 0)
                    fileinfo->magic = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "repnum") == 0)
                    fileinfo->repnum = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "snap_rollback") == 0)
                    fileinfo->snap_rollback = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "snap_version") == 0)
                    fileinfo->snap_version = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "reference") == 0)
                    fileinfo->reference = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "attr") == 0)
                    fileinfo->attr = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "priority") == 0)
                    fileinfo->priority = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "size") == 0)
                    fileinfo->size = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "mode") == 0)
                    fileinfo->mode = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "uid") == 0)
                    fileinfo->uid = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "gid") == 0)
                    fileinfo->gid = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "ctime") == 0)
                    fileinfo->ctime = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "mtime") == 0)
                    fileinfo->mtime = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "btime") == 0)
                    fileinfo->btime = lua_tointeger(L, -1);
                else if (strcmp(lua_tostring(L, -2), "atime") == 0)
                    fileinfo->atime = lua_tointeger(L, -1);
                else printf("error");
            }
        }
        else
        {
            int index = lua_gettop(L);
            const char *tag = lua_tostring(L, -2);
            fileinfo_table(L, index, fileinfo, tag);
        }
        lua_pop(L,1);
    }
}

void setattr_table(lua_State* L,int idx,setattr_t* setattr, const char *name) {
    lua_pushnil(L);
    while(lua_next(L, idx))
    {
        if(lua_type(L, -1) != LUA_TTABLE)
        {
            if(strcmp(lua_tostring(L,-2), "mode") == 0)
                setattr->mode.mode = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "uid") == 0)
                setattr->uid.uid = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "gid") == 0)
                setattr->gid.gid = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "size") == 0)
                setattr->size.size = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "replica") == 0)
                setattr->replica.replica = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "clone") == 0)
                setattr->clone.clone = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "protect") == 0)
                setattr->protect.protect = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "priority") == 0)
                setattr->priority.priority = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "localize") == 0)
                setattr->localize.localize = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "multpath") == 0)
                setattr->multpath.multpath = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "writeback") == 0)
                setattr->writeback.writeback = lua_tointeger(L,-1);
            else if(strcmp(lua_tostring(L,-2), "set_it") == 0) {
                if (strcmp(name, "mode") == 0)
                    setattr->mode.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "uid") == 0)
                    setattr->uid.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "gid") == 0)
                    setattr->gid.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "size") == 0)
                    setattr->size.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "replica") == 0)
                    setattr->replica.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "clone") == 0)
                    setattr->clone.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "protect") == 0)
                    setattr->protect.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "priority") == 0)
                    setattr->priority.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "localize") == 0)
                    setattr->localize.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "multpath") == 0)
                    setattr->multpath.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "writeback") == 0)
                    setattr->writeback.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "atime") == 0)
                    setattr->atime.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "btime") == 0)
                    setattr->btime.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "ctime") == 0)
                    setattr->ctime.set_it = lua_tointeger(L, -1);
                else if(strcmp(name, "mtime") == 0)
                    setattr->mtime.set_it = lua_tointeger(L, -1);
            }
            else if(strcmp(lua_tostring(L,-2), "seconds") == 0) {
                if (strcmp(name, "atime") == 0)
                    setattr->atime.time.seconds = lua_tointeger(L, -1);
                else if(strcmp(name, "btime") == 0)
                    setattr->btime.time.seconds = lua_tointeger(L, -1);
                else if(strcmp(name, "ctime") == 0)
                    setattr->ctime.time.seconds = lua_tointeger(L, -1);
                else if(strcmp(name, "mtime") == 0)
                    setattr->mtime.time.seconds = lua_tointeger(L, -1);
            }
            else if(strcmp(lua_tostring(L,-2), "nseconds") == 0) {
                if (strcmp(name, "atime") == 0)
                    setattr->atime.time.nseconds = lua_tointeger(L, -1);
                else if(strcmp(name, "btime") == 0)
                    setattr->btime.time.nseconds = lua_tointeger(L, -1);
                else if(strcmp(name, "ctime") == 0)
                    setattr->ctime.time.nseconds = lua_tointeger(L, -1);
                else if(strcmp(name, "mtime") == 0)
                    setattr->mtime.time.nseconds = lua_tointeger(L, -1);
            }
        }
        else
        {
            int index = lua_gettop(L);
            const char *tag = lua_tostring(L, -2);
            if(strcmp(tag, "atime") == 0)
                setattr_table(L, index, setattr, "atime");
            else if(strcmp(tag, "btime") == 0)
                setattr_table(L, index, setattr, "btime");
            else if(strcmp(tag, "ctime") == 0)
                setattr_table(L, index, setattr, "ctime");
            else if(strcmp(tag, "mtime") == 0)
                setattr_table(L, index, setattr, "mtime");
             setattr_table(L, index, setattr, tag);
        }
        lua_pop(L,1);
    }

}

void fileid_table(lua_State* L,  fileid_t* fid) {
    int idx = lua_gettop(L);
    lua_pushnil(L);
    while(lua_next(L, idx))
    {
        if(lua_type(L, -1) != LUA_TTABLE) {
            if (strcmp(lua_tostring(L, -2), "id") == 0) {
                fid->id = lua_tointeger(L, -1);
            } else if (strcmp(lua_tostring(L, -2), "type") == 0) {
                fid->type = lua_tointeger(L, -1);
            } else if (strcmp(lua_tostring(L, -2), "idx") == 0) {
                fid->idx = lua_tointeger(L, -1);
            }
        }
        lua_pop(L, 1);
    }
}

int l_stor_lookup1(lua_State* L) {
    int ret;
    chkid_t chkid;
    const char *vol = luaL_checkstring(L, 1);

    ret = stor_lookup1(vol, &chkid);
    if (unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }

    send_fileid_table(L, chkid);

    return 1;
}

int l_stor_splitpath(lua_State* L) {
    int ret;
    char name[MAX_NAME_LEN], buf[MAX_NAME_LEN];
    fileid_t fid;
    const char *str = luaL_checkstring(L, 1);

    ret = stor_splitpath("default", str, &fid, name);
    if(unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }

    send_fileid_table(L, fid);

    return 1;
}

int l_md_getattr(lua_State* L) {
    fileinfo_t fileinfo;
    int ret;
    fileid_t parent;

    fileid_table(L, &parent);

    ret = md_getattr(&parent, &fileinfo);
    if (unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }

    send_fileinfo_table(L, fileinfo);

    return 1;
}

int l_md_initattr(lua_State* L) {
    setattr_t *tmp;
    fileinfo_t fileinfo;
    char buf[MAX_MSG_SIZE];
    int idx;

    tmp = (void *)buf;
    md_initattr((void *)tmp, __S_IFDIR, 0);

    idx = lua_gettop(L);
    fileinfo_table(L, idx, &fileinfo, NULL);

    md_info2attr((void *)tmp, &fileinfo);

    send_setattr_table(L, tmp);

    return 1;
}

int l_md_mkpool_with_area(lua_State* L) {
    int ret;
    chkinfo_t *chkinfo;
    char buf[MAX_MSG_SIZE];
    setattr_t tmp;
    fileid_t parent;
    const char* name;
    const char* site_name;
    const char *s;

    lua_pushnil(L);
    while (lua_next(L, -2)) {
        s = lua_tostring(L, -2);
        if(strcmp(s, "attr") == 0) {
            setattr_table(L, 1, &tmp, NULL);
        } else if(strcmp(s, "fid") == 0) {
            fileid_table(L, &parent);
        } else if(strcmp(s, "name") == 0) {
            name = lua_tostring(L,-1);
        } else if(strcmp(s, "site_name") == 0) {
            site_name = lua_tostring(L,-1);
        }
        lua_pop(L, 1);
    }

    chkinfo = (void *)buf;

    ret = md_mkpool_with_area(&parent, name, site_name, &tmp, chkinfo);
    if(unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }
    return 1;
}

int l_md_mkvol(lua_State* L) {
    int ret, repnum;
    chkinfo_t *chkinfo;
    char buf[MAX_MSG_SIZE];
    setattr_t tmp;
    fileid_t parent;
    const char* name;
    const char* site_name;
    const char *s;

    lua_pushnil(L);
    while (lua_next(L, -2)) {
        s = lua_tostring(L, -2);
        if(strcmp(s, "attr") == 0) {
            setattr_table(L, 1, &tmp, NULL);
        } else if(strcmp(s, "fid") == 0) {
            fileid_table(L, &parent);
        } else if(strcmp(s, "name") == 0) {
            name = lua_tostring(L, -1);
        } else if(strcmp(s, "site_name") == 0) {
            site_name = lua_tostring(L, -1);
        } else if(strcmp(s, "repnum") == 0) {
            repnum = lua_tointeger(L, -1);
        }
        lua_pop(L, 1);
    }

    if (repnum >= LICH_REPLICA_MIN && repnum <= LICH_REPLICA_MAX) {
        tmp.replica.set_it = 1;
        tmp.replica.replica = repnum;
    }

    chkinfo = (void *)buf;

    ret = md_mkvol(&parent, name, site_name, &tmp, chkinfo);
    if (unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }
    return 1;
}

int l_md_snapshot_create_with_area(lua_State* L) {
    int ret;
    fileid_t parent;
    const char* name;
    const char* site_name;
    const char *s;

    lua_pushnil(L);
    while (lua_next(L, -2)) {
        s = lua_tostring(L, -2);
        if(strcmp(s, "fid") == 0) {
            fileid_table(L, &parent);
        } else if(strcmp(s, "name") == 0) {
            name = lua_tostring(L, -1);
        } else if(strcmp(s, "site_name") == 0) {
            site_name = lua_tostring(L, -1);
        }
        lua_pop(L, 1);
    }

    ret = md_snapshot_create_with_area(&parent, name, 0, site_name);
    if (unlikely(ret)) {
        lua_pushnil(L);
        return 1;
    }
    return 1;
}

static luaL_Reg lich4apilib[] = {
        {"env_init_simple",        l_env_init_simple},
        {"network_connect_master", l_network_connect_master},
        {"stor_init",              l_stor_init},
        {"node_getinfo",           l_node_getinfo},
        {"admin_listnode",         l_admin_listnode},
        {"net_gethostname",        l_net_gethostname},
        {"dispatch_sysstat",       l_dispatch_sysstat},
        {"md_getattr",             l_md_getattr},
        {"md_initattr",            l_md_initattr},
        {"md_mkpool_with_area",    l_md_mkpool_with_area},
        {"stor_splitpath",         l_stor_splitpath},
        {"md_mkvol",               l_md_mkvol},
        {"stor_lookup1",           l_stor_lookup1},
        {"md_snapshot_create_with_area", l_md_snapshot_create_with_area},
        {NULL,                     NULL}
};

int luaopen_lich4api(lua_State* L) {
    lua_newtable(L);
    luaL_setfuncs(L, lich4apilib, 0);
    return 1;
}
